import weakref
import pattern.crossref
from pattern.circuit import Control, Event
from pattern.crossref import CrossReference
from pattern.serialization.picklers import ShapesPickler


class entry(Control):

    def incoming(self, event):
        if event.point.tag == "from_remote":
            (msg, hook_index, data), = event.data
            whook = self.hooks[hook_index]
            # - synthesize an event from the hook outwards
            e = Event()
            e._weak_shape = weakref.ref(whook().shape())
            e._weak_window = weakref.ref(event.window)
            for onwards in event.window.acquireConnectionsAt(event, whook):
                onwards.push(*data)

    def emit(self, event):
        if event.slot == "shutdown": return 
        
        bridge = self.acquireInterface(event, "to_remote")
        if bridge is not None:
            def delegate_event(shape_id, slot, kwargs):
                shape = remote_circuit.by_id[shape_id]
                remote_circuit.call(shape, slot,
                    **kwargs)
            if 'data' in event.kwargs:
                kwargs = dict(data=event.kwargs['data'])
            else:
                kwargs = {}
            import algonquin, plugins, codebase
            bridge.push(self.import_package.im_func, ('algonquin', algonquin.__path__[0],))
            bridge.push(self.import_package.im_func, ('plugins', plugins.__path__[0],))
            bridge.push(self.import_module.im_func, ('codebase', codebase.__file__,))
            bridge.push(self.reconstruct.im_func, self.construct(event))
            bridge.push(delegate_event, (event.for_shape._id, event.slot, kwargs))
        elif hasattr(event.window, 'main'):
            event.window.call(event.for_shape, event.slot, **event.kwargs)
            

    def construct(self, event):
        subcomponents = set([event.shape])
        rejected = set()
        self.transitively_close(target_set=subcomponents,
            source_set=[bridge.connector.points()[1].shape()
                        for bridge in self.acquireSubcomponents(event)],
            reject_set=rejected,
            prune=set([event.shape]))
        for component in subcomponents: component._id = id(component)
        hooks = [hook for connector in rejected
                 if isinstance(connector, event.window.Connector)
                 for hook in connector.points()
                 if hook in subcomponents]
        self.hooks = [weakref.ref(h) for h in hooks]
        try:
            topleft, anchor = event.shape.topleft, event.shape.anchor
            event.shape.topleft = event.window.AbsolutePoint(0,0)
            event.shape.anchor = pattern.crossref.CrossReference(event.shape, event.shape.topleft)
            return (ShapesPickler.dumps((subcomponents, hooks)),)
        finally:
            event.shape.topleft, event.shape.anchor = topleft, anchor

    def transitively_close(self, target_set, source_set, reject_set, prune=()):
        for shape in source_set:
            if shape not in target_set:
                target_set.add(shape)
                if shape in reject_set: reject_set.remove(shape)
                refs = [val() for val in shape.__dict__.values() if isinstance(val, CrossReference) and val() not in prune]
                xrefs = []
                for xref in (xshape() for xshape in getattr(shape, 'xref', ())):
                    xref_refs = (val() for val in xref.__dict__.values() if isinstance(val, CrossReference))
                    if target_set.issuperset(xref_refs):
                        xrefs.append(xref)
                    else:
                        reject_set.add(xref)
                self.transitively_close(target_set, refs + xrefs, reject_set, prune)
    
    def import_module(name, path):
        import sys, imp
        if path.endswith(".pyc"): path = path[:-1]
        if name not in sys.modules:
            imp.load_source(name, path)
            
    def import_package(name, path):
        import sys, imp
        if name not in sys.modules:
            imp.load_package(name, path)
            
    def reconstruct(definition):
        from algonquin.circuit import Circuit
        import pickle

        subcomponents, hooks = pickle.loads(definition)

        class RemoteCircuit(Circuit):
            class TalkbackBridge(object):
                def __init__(self, hook_id):
                    self.hook_id = hook_id
                def push(self, *args):
                    import sys, pickle
                    pickle.dump(("hook", self.hook_id, args), sys.pout)
                    sys.pout.flush()
            def acquireConnectionsAt(self, event, connectionpoint):
                for c in Circuit.acquireConnectionsAt(self, event, connectionpoint):
                    yield c
                if connectionpoint() in self.hooks:
                    print "Outwards!"
                    yield self.TalkbackBridge(self.hooks.index(connectionpoint()))
            
        # - create the remote (inner) circuit
        global remote_circuit
        remote_circuit = RemoteCircuit()
        remote_circuit.shapes = list(subcomponents)
        remote_circuit.hooks = hooks
        remote_circuit.by_id = dict([(c._id, c) for c in remote_circuit.shapes])
        # - some cleanup
        for shape in remote_circuit.shapes:
            if None in shape.xref: del shape.xref[None]
        # - find main component
        for subcomponent in remote_circuit.shapes:
            if getattr(subcomponent, "tag", None) == "main":
                remote_circuit.main = subcomponent
        # - realize all shapes
        remote_circuit.populate(remote_circuit.shapes)
